from django.shortcuts import render
from django_redis import get_redis_connection
from decimal import Decimal
import json
from django import http
from django.utils import timezone
from django.db import transaction

from meiduo_mall.utils.views import LoginRequiredView
from goods.models import SKU
from .models import OrderInfo, OrderGoods
from users.models import Address
from meiduo_mall.utils.response_code import RETCODE
import logging

logger = logging.getLogger('django')

# 不要对当前正遍历的列表或字典直接做删除及修改操作
class OrderSettlementView(LoginRequiredView):
    """结算订单"""

    def get(self, request):
        """提供订单结算页面"""
        # 获取登录用户
        user = request.user
        # 查询地址信息

        addresses = Address.objects.filter(user=request.user, is_deleted=False)

        # 如果地址为空，渲染模板时会判断，并跳转到地址编辑页面
        # addresses = addresses or None

        # 从Redis购物车中查询出被勾选的商品信息
        redis_conn = get_redis_connection('carts')
        redis_cart = redis_conn.hgetall('carts_%s' % user.id)
        cart_selected = redis_conn.smembers('selected_%s' % user.id)
        cart = {}  # {1: 1}
        # {1: 1, 2: 1}
        # {1}
        for sku_id in cart_selected:
            cart[int(sku_id)] = int(redis_cart[sku_id])

        # 准备初始值
        total_count = 0
        total_amount = Decimal(0.00)
        # 查询商品信息
        skus = SKU.objects.filter(id__in=cart.keys())
        for sku in skus:
            sku.count = cart[sku.id]
            sku.amount = sku.count * sku.price
            # 计算总数量和总金额
            total_count += sku.count
            total_amount += sku.count * sku.price
        # 补充运费
        freight = Decimal('10.00')

        # 渲染界面
        context = {
            'addresses': addresses,  # 收货地址
            'skus': skus,  # 购物车中勾选商品信息
            'total_count': total_count,  # 勾选商品总数量
            'total_amount': total_amount,  # 要购买商品总金额
            'freight': freight,  # 运费
            'payment_amount': total_amount + freight  # 实付金额
        }

        return render(request, 'place_order.html', context)


class OrderCommitView(LoginRequiredView):
    """提交订单"""

    def post(self, request):
        # 接收
        json_dict = json.loads(request.body.decode())
        address_id = json_dict.get('address_id')
        pay_method = json_dict.get('pay_method')
        # 校验
        if all([address_id, pay_method]) is False:
            return http.HttpResponseForbidden('缺少必传参数')
        user = request.user
        try:
            address = Address.objects.get(id=address_id, is_deleted=False, user=user)
        except Address.DoesNotExist:
            return http.HttpResponseForbidden('address_id不存在')
        if pay_method not in [OrderInfo.PAY_METHODS_ENUM['CASH'], OrderInfo.PAY_METHODS_ENUM['ALIPAY']]:
            return http.HttpResponseForbidden('支付方式有误')

        # 订单编号: 时间 + user.id  2020010516470000000001
        order_id = timezone.now().strftime('%Y%m%d%H%M%S') + '%09d' % user.id
        # 判断动态订单状态 如果用户选择支付宝支付 待支付  货到付款,待发货
        status = (OrderInfo.ORDER_STATUS_ENUM['UNPAID']
                  if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY']
                  else OrderInfo.ORDER_STATUS_ENUM['UNSEND'])
        # 开启一个事务
        with transaction.atomic():
            # 创建事务保存点
            save_point = transaction.savepoint()
            try:
                # 一. 新增OrderInfo(订单基本信息表)记录
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal('0.00'),
                    freight=Decimal('10.00'),
                    pay_method=pay_method,
                    status=status,
                )
                # 创建redis连接
                redis_conn = get_redis_connection('carts')
                # 获取购物车hash数据
                redis_cats = redis_conn.hgetall('carts_%s' % user.id)
                # 获取购物车set数据
                selected_ids = redis_conn.smembers('selected_%s' % user.id)
                # 对hash数据做过滤,只留下勾选商品的id和count
                cart_dict = {}  # 用来包装要购买商品id和count
                for sku_id_bytes in selected_ids:
                    cart_dict[int(sku_id_bytes)] = int(redis_cats[sku_id_bytes])

                # for遍历下单
                for sku_id in cart_dict:
                    while True:
                        # 通过sku_id查询sku模型
                        sku = SKU.objects.get(id=sku_id)
                        # 当前商品要购买几件
                        buy_count = cart_dict[sku_id]
                        # 获取当前要购买商品原库存和销量
                        origin_stock = sku.stock
                        origin_sales = sku.sales

                        # import time
                        # time.sleep(5)
                        # 判断库存是否充足
                        if buy_count > origin_stock:
                            # 库存不足,事务回滚
                            transaction.savepoint_rollback(save_point)
                            return http.JsonResponse({'code': RETCODE.STOCKERR, 'errmsg': '库存不足'})
                        # 二. 修改SKU的库存和销量
                        new_stock = origin_stock - buy_count
                        new_sales = origin_sales + buy_count

                        # sku.stock = new_stock
                        # sku.sales = new_sales
                        # sku.save()
                        # 乐观锁
                        result = SKU.objects.filter(id=sku_id, stock=origin_stock, sales=origin_sales).update(stock=new_stock, sales=new_sales)
                        if result == 0:  # 有抢夺
                            continue
                        # 三. 修改SPU的销量
                        spu = sku.spu
                        spu.sales += buy_count
                        spu.save()
                        # 四. 新增N个 OrderGoods(订单商品信息) 记录
                        OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=buy_count,
                            price=sku.price
                        )

                        # 修改订单中商品总数量
                        order.total_count += buy_count
                        # 修改订单中总金额
                        order.total_amount += (sku.price * buy_count)
                        break  # 某一个商品下单成功后再去买下一个
                # 累加运费只需要一个订单一次
                order.total_amount += order.freight
                order.save()
            except Exception as e:
                logger.error(e)
                # 中间代码出现任何异常都回滚
                transaction.savepoint_rollback(save_point)
                return http.JsonResponse({'code': RETCODE.STOCKERR, 'errmsg': '下单失败'})
            else:
                # 订单没有问题提交事务
                transaction.savepoint_commit(save_point)
            # 订单没有问题提交事务
            # transaction.savepoint_commit(save_point)
        # 删除redis中已下单的购物车数据
        # pl = redis_conn.pipeline()
        # pl.hdel('carts_%s' % user.id, *selected_ids)
        # pl.delete('selected_%s' % user.id)
        # pl.execute()
        # 响应
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'order_id': order_id})


class OrderSuccessView(LoginRequiredView):
    """订单成功"""
    def get(self, request):
        # 接收
        query_dict = request.GET
        order_id = query_dict.get('order_id')
        payment_amount = query_dict.get('payment_amount')
        pay_method = query_dict.get('pay_method')
        user = request.user
        try:
            OrderInfo.objects.get(user=user, order_id=order_id, total_amount=payment_amount, pay_method=pay_method)
        except OrderInfo.DoesNotExist:
            return http.HttpResponseForbidden('订单有误')

        context = {
            'order_id': order_id,
            'payment_amount': payment_amount,
            'pay_method': pay_method
        }
        return render(request, 'order_success.html', context)
